using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using Irc.Objects;
using Irc.Events;

namespace Irc
{
	[CLSCompliant(true)]
	public class Channel : Target
	{
		private string name;
		private List<Nickname> nicknames = new List<Nickname>(),
							   banlist = new List<Nickname>(),
							   invitelist = new List<Nickname>(),
							   exceptlist = new List<Nickname>();
		private ModeCollection modes = new ModeCollection();
		private Topic topic;
		private string key = String.Empty;
		private int limit = Int32.MaxValue;
		private Client owner = null;

		public string Name {
			internal set { name = value; }
			get { return name; }
		}
		public Topic Topic {
			internal set { topic = value; }
			get { return topic; }
		}
		public string Key {
			internal set { key = value; }
			get { return key; }
		}
		public int Limit {
			internal set { limit = value; }
			get { return limit; }
		}
		public ModeCollection Mode {
			get { return modes; }
		}
		public Collection<Nickname> Nicknames {
			get { return new Collection<Nickname>(nicknames); }
			internal set
			{
				lock(nicknames)
				{
					nicknames.Clear();
					nicknames.AddRange(value);
				}
			}
		}
		public Collection<Nickname> BanList {
			get { return new Collection<Nickname>(banlist); }
			internal set
			{
				lock(banlist)
				{
					banlist.Clear();
					banlist.AddRange(value);
				}
			}
		}
		public Collection<Nickname> InviteList {
			get { return new Collection<Nickname>(invitelist); }
			internal set
			{
				lock(invitelist)
				{
					invitelist.Clear();
					invitelist.AddRange(value);
				}
			}
		}
		public Collection<Nickname> ExceptionList {
			get { return new Collection<Nickname>(exceptlist); }
			internal set
			{
				lock(exceptlist)
				{
					exceptlist.Clear();
					exceptlist.AddRange(value);
				}
			}
		}
		public Client Owner {
			internal set { owner = value; }
			get { return owner; }
		}

		public event EventHandler<JoinEventArgs> Join;
		public event EventHandler<TopicEventArgs> TopicChange;
		public event EventHandler<ModeEventArgs> ModeChange;
		public event EventHandler<NickEventArgs> NickChange;
		public event EventHandler<MessageEventArgs> PublicMessage;
		public event EventHandler<MessageEventArgs> PublicNotice;
		public event EventHandler<MessageEventArgs> PublicAction;
		public event EventHandler<NicklistEventArgs> Nicklist;

		public Channel(Client owner, string name)
		{
			this.owner = owner;
			this.name = name;
		}
		internal void AddModeChange(IModeMessage message)
		{
			if(message is IModeAdd)
				this.modes += message.Mode;
			else if(message is IModeRemove)
				this.modes -= message.Mode;
			if(message.Mode.ModeType == ModeName.Limit)
				limit = ((IntMode)message.Mode).Parameter;
			if(message.Mode.ModeType == ModeName.Key)
				key = ((StringMode)message.Mode).Parameter;
			ModeEventArgs e = new ModeEventArgs();
			e.Mode = message;
			if(ModeChange != null)
				ModeChange(this, e);
		}
		internal void AddJoin(UserMove movement)
		{
			if(movement is UserJoin)
				Nicknames.Add(movement.User);
			else if(movement is UserPart)
				Nicknames.Remove(movement.User);
			nicknames.Sort();
			JoinEventArgs e = new JoinEventArgs();
			e.Movement = movement;
			if(Join != null)
				Join(this, e);
		}
		internal void AddTopic(Topic topic)
		{
			Topic = topic;
			TopicEventArgs e = new TopicEventArgs();
			e.Topic = topic;
			if(TopicChange != null)
				TopicChange(this, e);
		}
		internal void ChangeNick(Nickname oldnick, Nickname newnick)
		{
			int index = FindNick(oldnick.Name);
			if(index != -1)
			{
				nicknames[index].Name = newnick.Name;
				nicknames.Sort();
				NickEventArgs e = new NickEventArgs();
				e.OldNick = oldnick;
				e.NewNick = newnick;
				if(NickChange != null)
					NickChange(this, e);
			}
		}
		internal void OnPublicMessage(PublicMessage message)
		{
			MessageEventArgs e = new MessageEventArgs();
			e.Message = message;
			if(PublicMessage != null)
				PublicMessage(this, e);
		}
		internal void OnPublicNotice(PublicNotice message)
		{
			MessageEventArgs e = new MessageEventArgs();
			e.Message = message;
			if(PublicNotice != null)
				PublicNotice(this, e);
		}
		internal void OnPublicAction(PublicAction message)
		{
			MessageEventArgs e = new MessageEventArgs();
			e.Message = message;
			if(PublicAction != null)
				PublicAction(this, e);
		}
		internal void OnJoinNicklist(Nicklist nicklist)
		{
			NicklistEventArgs e = new NicklistEventArgs();
			e.Nicklist = nicklist;
			if(Nicklist != null)
				Nicklist(this, e);
		}
		internal int FindNick(string name)
		{
			return nicknames.FindIndex(delegate(Nickname n){return n.Name == name;});
		}
		public void Say(string message)
		{
			PublicMessage m = new PublicMessage();
			m.Text = message;
			m.Target = this;
			Owner.Say(m);
		}
		public void Act(string message)
		{
			Message m = new Message();
			m.Text = message;
			m.Target = this;
			Owner.Act(m);
		}
		public void Notice(string message)
		{
			Message m = new Message();
			m.Text = message;
			m.Target = this;
			Owner.Notice(m);
		}
		public void JoinThis()
		{
			if(Mode.Contains(ModeName.Key))
				Owner.JoinChannel(Name, Key);
			else
				Owner.JoinChannel(Name);
		}
		public void Part()
		{
			Owner.SendMessage(String.Format(System.Globalization.CultureInfo.CurrentCulture, Strings.PartMessage, Name));
		}
		public void Rejoin()
		{
			Part();
			JoinThis();
		}
		public void ChangeTopic(string topic)
		{
			Topic t = new Topic();
			t.Text = topic;
			t.Target = this;
			Owner.ChangeTopic(t);
		}
		public void SetMode(ModeName mode, object parameter)
		{
			ChannelModeAdd message = new ChannelModeAdd();
			if(parameter is String)
				message.Mode = new StringMode(this, mode, ((string)parameter));
			else if(parameter is Int32)
				message.Mode = new IntMode(this, mode, ((int)parameter));
			else if(parameter is Nickname)
				message.Mode = new NicknameMode(this, mode, ((Nickname)parameter));
			else if(parameter == null)
				message.Mode = new Mode(this, mode);
			message.Sender = Owner.Nickname;
			message.Time = DateTime.Now;
			Owner.ChangeMode(message);
		}
		public void UnsetMode(ModeName mode, object parameter)
		{
			ChannelModeRemove message = new ChannelModeRemove();
			if(parameter is String)
				message.Mode = new StringMode(this, mode, ((string)parameter));
			else if(parameter is Int32)
				message.Mode = new IntMode(this, mode, ((int)parameter));
			else if(parameter is Nickname)
				message.Mode = new NicknameMode(this, mode, ((Nickname)parameter));
			else if(parameter == null)
				message.Mode = new Mode(this, mode);
			message.Sender = Owner.Nickname;
			message.Time = DateTime.Now;
			Owner.ChangeMode(message);
		}
		public override string ToString()
		{
			return Name;
		}
	}
}
